参考自 Git 官方文档:7.11 Git 工具 - 子模块

开始使用子模块

将一个已存在的 Git 仓库添加为正在工作的仓库的子模块

1
$ git submodule add <子模块仓库地址>

克隆含有子模块的项目

普通的git clone命令默认会包含子模块的目录,但其中还没有任何文件,需要执行 2 个命令:

1
2
3
4
# 初始化本地配置文件
$ git submodule init
# 从该项目中抓取所有数据并检出父项目中列出的合适的提交
$ git submodule update

更简单的方式:

1
2
# 会自动初始化并更新仓库中的每一个子模块
$ git clone --recursive <包含子模块的仓库地址>

在包含子模块的项目上工作

拉取上游修改

  1. 进入子模块目录执行:git fetchgit merge
  2. 直接在主项目目录下执行:
1
2
# Git 默认会尝试更新所有子模块,所以如果有很多子模块的话,你可以传递想要更新的子模块的名字
$ git submodule update --remote <--rebase><--merge>

如果你忘记  –rebase  或  –merge,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的 HEAD 状态

即便这真的发生了也不要紧,你只需回到目录中再次检出你的分支(即还包含着你的工作的分支)然后手动地合并或变基  origin/stable(或任何一个你想要的远程分支)就行了。

修改子模块的默认分支

1
2
# 例如要修改子模块的默认分支到develop上
$ git config -f .gitmodules submodule.子模块名.branch develop

其实就是修改了.gitmodule这个文件。提交以后,当其他人执行git submodule update --remote后,就会从子模块拉取 develop 这个新分支(但是还得手动进入子模块目录后切到 develop 分支- -)

发布子模块改动

如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦,因为他们无法得到依赖的子模块改动。 那些改动只存在于我们本地的拷贝中。

所以我们可以:

1. 让 Git 在推送到主项目前检查所有子模块是否已推送,如果任何提交的子模块改动没有推送那么 “check” 选项会直接使  push  操作失败:

1
$ git push --recurse-submodules=check

2. 让 Git 进入到每个子模块中然后在推送主项目前推送。如果那个子模块因为某些原因推送失败,主项目也会推送失败。

1
$ git push --recurse-submodules=on-demand

合并子模块改动

子模块技巧

子模块遍历

有一个  foreach  子模块命令,它能在每一个子模块中运行任意命令。 如果项目中包含了大量子模块,这会非常有用。

1
$ git submodule foreach 'git pull'

子模块的问题

  • 问题一

在有子模块的项目中切换分支可能会造成麻烦。 如果你创建一个新分支,在其中添加一个子模块,之后切换到没有该子模块的分支上时,你仍然会有一个还未跟踪的子模块目录。

  • 解决方案

如果你移除它然后切换回有那个子模块的分支,需要运行  submodule update –init  来重新建立和填充。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git clean -fdx
Removing CryptoLibrary/

$ git checkout add-crypto
Switched to branch 'add-crypto'

$ ls CryptoLibrary/

$ git submodule update --init
Submodule path 'CryptoLibrary': checked out 'b8dda6aa182ea4464f3f3264b11e0268545172af'

$ ls CryptoLibrary/
Makefile includes scripts src
  • 问题二

如果你在项目中已经跟踪了一些文件,然后想要将它们移动到一个子模块中,那么请务必小心,否则 Git 会对你发脾气。 假设项目内有一些文件在子目录中,你想要将其转换为一个子模块。 如果删除子目录然后运行  submodule add,Git 会朝你大喊:

1
2
3
$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' already exists in the index
  • 解决方案

你必须要先取消暂存  CryptoLibrary  目录。 然后才可以添加子模块:

1
2
3
4
5
6
7
8
$ git rm -r CryptoLibrary
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.